package com.fzhucc.modules.security.rest

import cn.hutool.core.util.IdUtil
import com.fzhucc.annotation.Log
import com.fzhucc.annotation.rest.AnonymousDeleteMapping
import com.fzhucc.annotation.rest.AnonymousGetMapping
import com.fzhucc.annotation.rest.AnonymousPostMapping
import com.fzhucc.config.RsaProperties
import com.fzhucc.exception.BadRequestException
import com.fzhucc.modules.security.config.WxProperties
import com.fzhucc.modules.security.config.bean.LoginCodeEnum
import com.fzhucc.modules.security.config.bean.LoginProperties
import com.fzhucc.modules.security.config.bean.SecurityProperties
import com.fzhucc.modules.security.domain.OnlineUser
import com.fzhucc.modules.security.domain.dto.*
import com.fzhucc.modules.security.repository.UserCodeRepository
import com.fzhucc.modules.security.security.TokenProvider
import com.fzhucc.modules.security.service.AuthenticationService
import com.fzhucc.modules.security.service.OnlineUserService
import com.fzhucc.modules.system.repository.UserRepository
import com.fzhucc.utils.*
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import lombok.RequiredArgsConstructor
import lombok.extern.slf4j.Slf4j
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Controller
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
import java.util.concurrent.TimeUnit
import javax.annotation.Resource
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

/**
 * @author Zheng Jie
 * @date 2018-11-23
 * 授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
@Api(tags = ["系统：系统授权接口"])
class AuthorizationController(
    private val properties: SecurityProperties,
    private val redisUtils: RedisUtils,
    private val onlineUserService: OnlineUserService,
    private val tokenProvider: TokenProvider,
    private val userCodeRepository: UserCodeRepository,
    private val userRepository: UserRepository,
    @Resource
    private val loginProperties: LoginProperties,
    private val authenticationService: AuthenticationService,
    private val wxProperties: WxProperties
) {


    @Log("用户登录")
    @ApiOperation("登录授权")
    @AnonymousPostMapping(value = ["/login"])
    fun login(@Validated @RequestBody authUser: AuthUserDto, request: HttpServletRequest): ResponseEntity<Any> {
        // 密码解密
        val password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.password)
        if(authUser.code != null && authUser.code!="") {
            // 查询验证码
            val code = redisUtils.get(authUser.uuid) as String
            // 清除验证码
            redisUtils.del(authUser.uuid)
            if (StringUtils.isBlank(code)) {
                throw BadRequestException("验证码不存在或已过期")
            }
            if (StringUtils.isBlank(authUser.code) || !authUser.code.equals(code, ignoreCase = true)) {
                throw BadRequestException("验证码错误")
            }
        }

        val authDto = authenticationService.login(authUser.username, password, request)
        return ResponseEntity.ok(authDto)
    }


    @GetMapping("/info")
    @ApiOperation("获取用户信息")
    fun getUserInfo(): ResponseEntity<JwtUserDto> {
        return ResponseEntity.ok(JwtSecurityUtils.getCurrentUser())
    }

    // 获取运算的结果
    @AnonymousGetMapping("/code")
    @ApiOperation("获取验证码")
    fun getCode(): ResponseEntity<Any> {
        // 获取运算的结果
        val captcha = loginProperties.captcha
        val uuid = properties.codeKey + IdUtil.simpleUUID()
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        var captchaValue = captcha.text()
        if (captcha.charType - 1 == LoginCodeEnum.ARITHMETIC.ordinal && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
        }
        // 保存
        redisUtils[uuid, captchaValue, loginProperties.loginCode.expiration] = TimeUnit.MINUTES
        // 验证码信息
        val imgResult: Map<String, Any> = object : HashMap<String, Any>(2) {
            init {
                put("img", captcha.toBase64())
                put("uuid", uuid)
            }
        }
        return ResponseEntity.ok(imgResult)
    }

    @ApiOperation("退出登录")
    @AnonymousDeleteMapping("/logout")
    fun logout(request: HttpServletRequest): ResponseEntity<Any> {
        onlineUserService.logout(tokenProvider.getToken(request))
        return ResponseEntity(HttpStatus.OK)
    }

    @AnonymousGetMapping("/checkCode")
    @ResponseBody
    fun checkCode(@RequestParam code: String, request: HttpServletRequest): ResponseEntity<Any> {
        val res = userCodeRepository.findByCode(code)
        if (res == null) {
            throw BadRequestException("校验失败")
        }
        val authentication: Authentication = authenticationService.authenticateByUnionIdPhone(res.phone)
        SecurityContextHolder.getContext().authentication = authentication
        val apiToken = tokenProvider.createToken(authentication)
        val jwtUserDto = authentication.principal as JwtUserDto
        // 保存在线信息
        onlineUserService.save(jwtUserDto, OnlineUser.SYSTEM, apiToken, request)
        // 返回 token 与 用户信息
        val authDto = AuthDto(
            properties.tokenStartWith + apiToken,
            jwtUserDto
        )
        if (loginProperties.isSingleLogin) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authDto.user.username, apiToken)
        }
        return ResponseEntity.ok(authDto)
    }
}